package com.millstein.tsinglog.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.millstein.tsinglog.commons.constant.SystemConstant;
import com.millstein.tsinglog.commons.enums.ResultCode;
import com.millstein.tsinglog.exception.TsinglogException;
import com.millstein.tsinglog.commons.service.BaseService;
import com.millstein.tsinglog.mapper.biz.NoticeMapper;
import com.millstein.tsinglog.model.dto.notice.NoticeSaveDTO;
import com.millstein.tsinglog.model.pojo.Notice;
import com.millstein.tsinglog.model.vo.result.PageResult;
import com.millstein.tsinglog.service.NoticeService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.*;

@Service
public class NoticeServiceImpl extends BaseService implements NoticeService {

    @Autowired
    private NoticeMapper noticeMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public PageResult<Notice> getNoticeByPage(Integer currentPage, Integer pageSize, String keyword, Short type) {
        // 1.处理参数
        if (currentPage == null) {
            currentPage = SystemConstant.DEFAULT_CURRENT_PAGE;
        }
        if (pageSize == null) {
            pageSize = SystemConstant.DEFAULT_PAGE_SIZE;
        }

        // 2.条件查询
        Example example = new Example(Notice.class);
        Example.Criteria criteria = example.createCriteria();

        if (StringUtils.isNotBlank(keyword)) {
            criteria.andLike("content", "%" + keyword + "%");
        }
        if (type != null) {
            criteria.andEqualTo("type", type);
        }

        // 3.过滤掉status为删除状态的记录
        criteria.andEqualTo("status", SystemConstant.STATUS_ENTITY_NORMAL);

        // 4.排序
        example.setOrderByClause("usable desc, type asc, update_time desc");

        // 5.开始查询
        PageHelper.startPage(currentPage, pageSize);
        List<Notice> list = noticeMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(list)) {
            throw new TsinglogException(ResultCode.RETURN_DATA_NOT_FOUND);
        }
        PageInfo pageInfo = new PageInfo(list);
        return new PageResult<Notice>(pageInfo.getTotal(), pageInfo.getPages(), list);
    }

    @Override
    public List<Notice> getAllBackUsableNotice() {

        Map<String, Object> criteria = new HashMap<String, Object>();
        criteria.put("status", SystemConstant.STATUS_ENTITY_NORMAL);
        criteria.put("usable", SystemConstant.USABLE_ENTITY_ON);
        criteria.put("type", SystemConstant.BACK_END);

        return this.getNoticeByConfig(criteria, SystemConstant.DEFAULT_CURRENT_PAGE,
                SystemConstant.PAGE_SIZE_FOR_NOTICE_OF_USABLE);

    }

    @Override
    public int getNoticeCountByType(Short type) {

        if (type == null) {
            throw new TsinglogException(ResultCode.PARAM_IS_BLANK);
        }

        Example example = new Example(Notice.class);
        Example.Criteria criteria = example.createCriteria();

        criteria.andEqualTo("type", type);
        criteria.andEqualTo("usable", SystemConstant.USABLE_ENTITY_ON);
        criteria.andEqualTo("status", SystemConstant.STATUS_ENTITY_NORMAL);

        return noticeMapper.selectCountByExample(example);
    }

    @Override
    public void changeUsable(Long id, Short usable) {
        if (id == null || usable == null) {
            throw new TsinglogException(ResultCode.PARAM_IS_BLANK);
        }

        Notice dbNotice = noticeMapper.selectNoticeByPrimaryKeyForServer(id);
        if (dbNotice == null) {
            throw new TsinglogException(ResultCode.HANDLE_DATA_NOT_EXISTED);
        }

        // 如果数据库中的公告是未启用，而现在需要启用的情况下，先查询启用的公告有没有到达最大数目
        if (dbNotice.getUsable() == SystemConstant.USABLE_ENTITY_OFF && usable == SystemConstant.USABLE_ENTITY_ON) {
            int count = this.getNoticeCountByType(dbNotice.getType());
            if (count >= SystemConstant.MAX_COUNT_OF_USABLE_NOTICE_FOR_EACH_TYPE) {
                throw new TsinglogException(ResultCode.LARGER_THAN_MAX_COUNT_OF_USABLE_NOTICE);
            }
        }

        dbNotice.setUsable(usable);
        dbNotice.setUpdateTime(getCurrentDateStr());

        int result = noticeMapper.updateByPrimaryKeySelective(dbNotice);
        if (result != 1) {
            throw new TsinglogException(ResultCode.UPDATE_FAILURE);
        }

        redisTemplate.delete(SystemConstant.REDIS_KEY_FOR_NOTICE_LIST);

    }

    @Override
    @Transactional
    public void deleteByIds(List<Long> ids) {

        if (CollectionUtils.isEmpty(ids)) {
            throw new TsinglogException(ResultCode.PARAM_IS_BLANK);
        }

        Notice notice = new Notice();
        notice.setUpdateTime(getCurrentDateStr());
        notice.setStatus(SystemConstant.STATUS_ENTITY_DELETE);

        for (Long id: ids) {
            notice.setId(id);

            int result = noticeMapper.updateByPrimaryKeySelective(notice);
            if (result != 1) {
                throw new TsinglogException(ResultCode.DELETE_FAILURE);
            }
        }

        redisTemplate.delete(SystemConstant.REDIS_KEY_FOR_NOTICE_LIST);
    }

    @Override
    public void insertNotice(NoticeSaveDTO noticeSaveDTO) {

        int count = this.getNoticeCountByType(noticeSaveDTO.getType());
        if (count >= SystemConstant.MAX_COUNT_OF_USABLE_NOTICE_FOR_EACH_TYPE) {
            throw new TsinglogException(ResultCode.LARGER_THAN_MAX_COUNT_OF_USABLE_NOTICE);
        }

        Notice notice = new Notice();
        notice.setContent(noticeSaveDTO.getContent());
        notice.setLink(noticeSaveDTO.getLinkHandled());
        notice.setType(noticeSaveDTO.getType());
        notice.setUsable(noticeSaveDTO.getUsable());
        notice.setStatus(SystemConstant.STATUS_ENTITY_NORMAL);
        notice.setCreateTime(getCurrentDateStr());
        notice.setUpdateTime(getCurrentDateStr());

        int result = noticeMapper.insert(notice);
        if (result != 1) {
            throw new TsinglogException(ResultCode.INSERT_FAILURE);
        }

        if (notice.getUsable() == SystemConstant.USABLE_ENTITY_ON) {
            redisTemplate.delete(SystemConstant.REDIS_KEY_FOR_NOTICE_LIST);
        }

    }

    @Override
    public void updateNotice(NoticeSaveDTO noticeSaveDTO) {

        Long id = noticeSaveDTO.getId();
        if (id == null) {
            throw new TsinglogException(ResultCode.PARAM_NOT_COMPLETE);
        }

//        int count = this.getNoticeCountByType(noticeSaveDTO.getType());
//        if (count >= SystemConstant.MAX_COUNT_OF_USABLE_NOTICE_FOR_EACH_TYPE) {
//            throw new TsinglogException(ResultEnum.LARGER_THAN_MAX_COUNT_OF_USABLE_NOTICE);
//        }

        Notice dbNotice = noticeMapper.selectNoticeByPrimaryKeyForServer(id);
        if (dbNotice == null) {
            throw new TsinglogException(ResultCode.HANDLE_DATA_NOT_EXISTED);
        }

        dbNotice.setContent(noticeSaveDTO.getContent());
        dbNotice.setLink(noticeSaveDTO.getLinkHandled());
        dbNotice.setType(noticeSaveDTO.getType());
        dbNotice.setUsable(noticeSaveDTO.getUsable());
        dbNotice.setUpdateTime(getCurrentDateStr());

        int result = noticeMapper.updateByPrimaryKey(dbNotice);
        if (result != 1) {
            throw new TsinglogException(ResultCode.UPDATE_FAILURE);
        }

        redisTemplate.delete(SystemConstant.REDIS_KEY_FOR_NOTICE_LIST);
    }

    @Override
    public Notice getNoticeById(Long id) {
        if (id == null) {
            throw new TsinglogException(ResultCode.PARAM_IS_BLANK);
        }

        Notice notice = noticeMapper.selectNoticeByPrimaryKeyForServer(id);
        if (notice == null) {
            throw new TsinglogException(ResultCode.RETURN_DATA_NOT_FOUND);
        }

        return notice;
    }

    @Override
    public List<Map<String, Object>> getNoticeListForPortal() {



        ValueOperations operations = redisTemplate.opsForValue();

        if (redisTemplate.hasKey(SystemConstant.REDIS_KEY_FOR_NOTICE_LIST)) {
            return (List<Map<String, Object>>) operations.get(SystemConstant.REDIS_KEY_FOR_NOTICE_LIST);
        }

        List<Map<String, Object>> noticeList = new ArrayList<>();
        List<Map<String, Object>> list = noticeMapper.getNoticeListForPortal(SystemConstant.DEFAULT_CURRENT_PAGE - 1,
                SystemConstant.MAX_COUNT_OF_USABLE_NOTICE_FOR_EACH_TYPE);

        if (!CollectionUtils.isEmpty(list)) {
            // 1.将数据中的link进行处理，有占位符的要把占位符换成前台的地址
            for (Map<String, Object> map : list) {
                // 没有link属性的情况
                // 由于前台thymeleaf不能对实体有无某个属性进行判断，所以在这里只能把这个属性手动加上去
                if (map.keySet().size() == 2) {
                    map.put("link", null);
                    noticeList.add(map);
                    continue;
                }
                // 有link属性的情况
                Map<String, Object> returnMap = new HashMap<>();
                for (Map.Entry<String, Object> item: map.entrySet()) {
                    if (item.getKey().equals("link")) {
                        Object oLink = item.getValue();
                        if (oLink != null) {
                            String link = (String) oLink;
                            if (link.contains(SystemConstant.PATH_PORTAL_PLACEHOLDER)) {
                                link = link.replace(SystemConstant.PATH_PORTAL_PLACEHOLDER, SystemConstant.PORTAL_URL);
                            }
                            returnMap.put(item.getKey(), link);
                        }
                    } else {
                        returnMap.put(item.getKey(), item.getValue());
                    }
                }
                noticeList.add(returnMap);
            }
            // 2.如果数据大于1条，那么要将第一条数据在returnList的末尾再插入一遍
            if (list.size() > 1) {
                noticeList.add(noticeList.get(0));
            }
        }

        // 处理完后将数据放到redis中
        operations.set(SystemConstant.REDIS_KEY_FOR_NOTICE_LIST, noticeList);


        return noticeList;
    }

    /**
     * 根据查询条件查询相应的公告信息
     * @param config 查询条件
     * @param currentPage 当前页
     * @param pageSize 每页的数据量
     * @return
     */
    private List<Notice> getNoticeByConfig(Map<String, Object> config, Integer currentPage, Integer pageSize) {
        Example example = new Example(Notice.class);
        Example.Criteria criteria = example.createCriteria();

        if (config != null) {
            Set<Map.Entry<String, Object>> entries = config.entrySet();
            for (Map.Entry<String, Object> entry: entries) {
                if ("status".equals(entry.getKey())) {
                    criteria.andEqualTo("status", entry.getValue());
                } else if ("usable".equals(entry.getKey())) {
                    criteria.andEqualTo("usable", entry.getValue());
                } else if ("type".equals(entry.getKey())) {
                    criteria.andEqualTo("type", entry.getValue());
                }
            }
        }
        // 根据修改时间的降序排列
        example.setOrderByClause("update_time desc");

        // 开始查询
        PageHelper.startPage(currentPage, pageSize);
        return noticeMapper.selectByExample(example);
    }
}
